#include "StdAfx.h"
#include "MillingMachine3C.h"
#include <GL\glew.h>
#include <algorithm>
#include <assert.h>
#include <exception>
#include <fstream>
using namespace std;


#define GET_MATERIAL_VALUE(materialValue,i,x,y,machine){materialValue = i==0?machine->materialSampling[ind]:machine->GetValueAt(x,y);}
#define FILL(material,index,x,y,z){material[pointInd++] = x;material[pointInd++] = y;material[pointInd++] = z;}
#define CONGESTION_WINDOW 1
#define DRAW_WINDOWS_COUNT 3

MillingMachine3C::MillingMachine3C(UINT _width,UINT _height,int _depth)
{
	this->width = _width;
	this->height = _height;
	this->initDepth =  _depth;
	this->levels = 2;
	glGetString(0);
	this->toolRotatingClockwise = false;
	this->m_nVBOVertices = 0;
	this->createMaterial();
	this->createIndices();
	this->createColours();
	this->createNormalVectors();
	this->createErrorTable();
}

MillingMachine3C::~MillingMachine3C(void)
{
	delete [] this->errorTable;
	//delete [] this->colours;
	//delete [] this->normals;
	glDeleteBuffers(1,&this->m_nNormalBurrerID);
	glDeleteBuffers(1,&this->m_nIndicesBufferID);
	glDeleteBuffers(1,&this->m_nColoursBufferID);
	glDeleteBuffers(1,&this->m_nVBOVertices);
}

void MillingMachine3C::fillDrawWindows(int count)
{
	this->drawWindow = new DrawWindow[count];
	this->drawWindow[0].north = &this->drawWindow[1];
	this->drawWindow[1].south = &this->drawWindow[0];
	this->drawWindow[1].north = &this->drawWindow[2];
	this->drawWindow[2].south = &this->drawWindow[1];
}

void MillingMachine3C::adjustCongestionWindow(DrawWindow *& windows,int congestionWindowId,const Point3D & p)
{
	int i=0;
	bool pointInside = false;

	MY_DATATYPE pointDiffX = (MY_DATATYPE)0.0;
	MY_DATATYPE pointDiffY = (MY_DATATYPE)0.0;
	MY_DATATYPE diffX = (MY_DATATYPE)0.0;
	MY_DATATYPE diffY = (MY_DATATYPE)0.0;

	while(!pointInside && i<DRAW_WINDOWS_COUNT)
	{
		if(windows[i].isPointInside(p))
		{
			diffX = windows[i].ruCorner.x - windows[i].llCorner.x;
			diffY = windows[i].ruCorner.y - windows[i].llCorner.y;
			pointDiffX = p.x - windows[i].llCorner.x;
			pointDiffY = p.y - windows[i].llCorner.y;
			pointInside = true;
		}
		++i;
	}

	if(i>=DRAW_WINDOWS_COUNT)
		return;

	--i;

	for(int j=0;j<i;++j)
	{
		MY_DATATYPE locDiffX;
		MY_DATATYPE locDiffY;
	}
}

int MillingMachine3C::getVerticesCount()
{
	return this->width*this->height;
}

void MillingMachine3C::createMaterial()
{
	UINT size = levels*getVerticesCount()*3;
	double * materialSampling = new double[size];

	int pointInd = 0;
	for	(int l = 0;l<levels;++l)
	{
		for(UINT i=0;i<this->height;++i)
		{
			for(UINT j=0;j<this->width;++j)
			{
				materialSampling[pointInd++] = j;
				materialSampling[pointInd++] = i;
				materialSampling[pointInd++] = l==0?this->initDepth:0.0;
			}
		}
	}

	glGenBuffers(1,&this->m_nVBOVertices);
	glBindBuffer(GL_ARRAY_BUFFER,this->m_nVBOVertices);
	glBufferData(GL_ARRAY_BUFFER,size *sizeof(double),materialSampling,GL_DYNAMIC_COPY);
	glBindBuffer(GL_ARRAY_BUFFER,0);
	delete[] materialSampling;
}

void MillingMachine3C::createIndices()
{
	UINT size = getVerticesCount();
	this->indicesNo =  levels* (this->width-1)*(this->height-1)*6;
	GLuint *  indices = new GLuint[this->indicesNo];
	UINT currInd = 0;
	for(int l = 0;l<this->levels;++l)
	{
		UINT ind =l*(this->width*this->height);
		for(UINT i=0;i<height-1;++i)
		{
			for(UINT j=0;j<this->width-1;++j)
			{
				indices[currInd++] = ind;
				indices[currInd++] = ind+1;
				indices[currInd++] = ind+this->width;

				indices[currInd++] = ind+this->width; 
				indices[currInd++] = ind+this->width+1;
				indices[currInd++] = ind+1;
				ind++;
			}
			ind++;
		}
	}
	glGenBuffers(1,&this->m_nIndicesBufferID);
	glBindBuffer(GL_ARRAY_BUFFER,this->m_nIndicesBufferID);
	glBufferData(GL_ARRAY_BUFFER,this->indicesNo*sizeof(GL_UNSIGNED_INT),indices, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER,0);
	delete [] indices;
}

void  MillingMachine3C::createColours()
{
	UINT size = 3*levels*this->getVerticesCount();
	GLubyte * colours = new GLubyte[size];
	int pointInd = 0;
	for(int l=0;l<this->levels;++l)
	{
		for(UINT i=0;i<this->width;i++)
		{
			for(UINT j=0;j<this->height;++j)
			{
				colours[pointInd++] = 250;
				colours[pointInd++] = 0;
				colours[pointInd++] = 0;
			}
		}
	}
	glGenBuffers(1,&this->m_nColoursBufferID);
	glBindBuffer(GL_COLOR_ARRAY,this->m_nColoursBufferID);
	glBufferData(GL_COLOR_ARRAY,size*sizeof(GLubyte),colours,GL_STATIC_DRAW);
	glBindBuffer(GL_COLOR_ARRAY,0);
	delete[] colours;
}

void MillingMachine3C::createNormalVectors()
{
	UINT size = this->getVerticesCount()*3*this->levels;
	GLdouble * normals = new GLdouble[size];
	UINT nIndex = 0;
	for(int l=0;l<this->levels;++l)
	{
		for(UINT i=0;i<this->height;++i)
		{
			for(UINT j=0;j<this->width;++j)
			{
				normals[nIndex++] = 0.0;
				normals[nIndex++] = 0.0;
				normals[nIndex++] = l==0?1.0:-1.0;
			}
		}
	}
	glGenBuffers(1,&this->m_nNormalBurrerID);
	glBindBuffer(GL_NORMAL_ARRAY,this->m_nNormalBurrerID);
	glBufferData(GL_NORMAL_ARRAY,size*sizeof(GLdouble),normals,GL_STATIC_DRAW);
	glBindBuffer(GL_NORMAL_ARRAY,0);
	delete [] normals;
}

void MillingMachine3C::createErrorTable()
{
	UINT size = 3*getVerticesCount();
	this->errorTable = new double[size];
	memset(this->errorTable,0,size*sizeof(double));
}

/*
index - z-coordinate of point
*/
void MillingMachine3C::updateNormal(UINT index)
{
	//todo: do dokonczenia
}

void MillingMachine3C::fillMachine(MY_DATATYPE *& material,UINT size)
{
	/*UINT i=0,ind = 0;
	UINT pointInd = 0;
	for(int i=0;i<2;++i)
	{
	while(ind<size)
	{
	if( ( ind + 1 ) % this->width != 0)
	{
	div_t rem = div((int)ind,this->width);
	int x,y;
	float materialValue = 0.0f;
	GET_COORDS(x,y,ind,this->width);
	GET_MATERIAL_VALUE(materialValue,i,x,y,this);
	FILL(material,pointInd,x,y,materialValue);

	GET_COORDS(x,y,ind+1,this->width);
	GET_MATERIAL_VALUE(materialValue,i,x,y,this);
	FILL(material,pointInd,x,y,materialValue);

	GET_COORDS(x,y,ind+this->width,this->width);
	GET_MATERIAL_VALUE(materialValue,i,x,y,this);
	FILL(material,pointInd,x,y,materialValue);

	GET_COORDS(x,y,ind+1,this->width);
	GET_MATERIAL_VALUE(materialValue,i,x,y,this);
	FILL(material,pointInd,x,y,materialValue);
	GET_COORDS(x,y,ind+this->width+1,this->width);
	GET_MATERIAL_VALUE(materialValue,i,x,y,this);
	FILL(material,pointInd,x,y,materialValue);
	GET_COORDS(x,y,ind+this->width,this->width);
	GET_MATERIAL_VALUE(materialValue,i,x,y,this);
	FILL(material,pointInd,x,y,materialValue);
	}
	ind++;
	}
	ind = 0;
	}
	int offsetY = this->width*this->height;
	for(int i=0;i<this->width-1;++i)
	{
	int x,y;
	float materialValue = 0.0f;

	GET_COORDS(x,y,i,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[i]);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	GET_COORDS(x,y,i+1,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[i]);

	GET_COORDS(x,y,i,this->width);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	GET_COORDS(x,y,i+1,this->width);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	GET_COORDS(x,y,i+1,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[i+1]);
	}*/

	/*	int offsetX =this->width*this->height - this->width;
	for(int i=0;i<this->width-1;++i)
	{
	int x,y;
	float materialValue = 0.0f;

	GET_COORDS(x,y,i+offsetX,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[i]);
	//glVertex3d(x,y,this->materialSampling[i]);
	//glVertex3d(x,y,this->GetValueAt(x,y));
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	GET_COORDS(x,y,i+1+offsetX,this->width);
	//glVertex3d(x,y,this->materialSampling[i]);
	FILL(material,pointInd,x,y,this->materialSampling[i]);

	GET_COORDS(x,y,i+offsetX,this->width);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	//glVertex3d(x,y,this->GetValueAt(x,y));
	GET_COORDS(x,y,i+1+offsetX,this->width);
	//glVertex3d(x,y,this->GetValueAt(x,y));
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	GET_COORDS(x,y,i+1+offsetX,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[i+1]);
	//glVertex3d(x,y,this->materialSampling[i+1]);
	}

	for(int i=0;i<this->height-1;++i)
	{
	int x,y;
	float materialValue = 0.0f;
	GET_COORDS(x,y,i*this->width,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[i]);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	/*glVertex3d(x,y,this->materialSampling[i]);
	glVertex3d(x,y,this->GetValueAt(x,y));*/
	/*		GET_COORDS(x,y,(i+1)*this->width,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[(i+1)*this->width]);
	/*glVertex3d(x,y,this->materialSampling[(i+1)*this->width]);*/

	/*		GET_COORDS(x,y,(i+1)*this->width,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[(i+1)*this->width]);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	/*glVertex3d(x,y,this->materialSampling[(i+1)*this->width]);
	glVertex3d(x,y,this->GetValueAt(x,y));*/
	/*		GET_COORDS(x,y,i*this->width,this->width);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	//glVertex3d(x,y,this->GetValueAt(x,y));
	}

	for(int i=0;i<this->height-1;++i)
	{
	int x,y;
	float materialValue = 0.0f;

	GET_COORDS(x,y,i*this->width+this->width-1,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[i*this->width+this->width-1]);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	//glVertex3d(x,y,this->materialSampling[i*this->width+this->width-1]);
	//glVertex3d(x,y,this->GetValueAt(x,y));
	GET_COORDS(x,y,(i+1)*this->width+this->width-1,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[(i+1)*this->width+this->width-1]);
	//glVertex3d(x,y,this->materialSampling[(i+1)*this->width+this->width-1]);

	GET_COORDS(x,y,(i+1)*this->width+this->width-1,this->width);
	FILL(material,pointInd,x,y,this->materialSampling[(i+1)*this->width+this->width-1]);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	/*glVertex3d(x,y,this->materialSampling[(i+1)*this->width+this->width-1]);
	glVertex3d(x,y,this->GetValueAt(x,y));*/
	/*		GET_COORDS(x,y,i*this->width+this->width-1,this->width);
	FILL(material,pointInd,x,y,this->GetValueAt(x,y));
	//glVertex3d(x,y,this->GetValueAt(x,y));
	}*/
}

MY_DATATYPE MillingMachine3C::GetValueAt(int x,int y)
{
	MY_DATATYPE result;
	glBindBuffer(GL_ARRAY_BUFFER,this->m_nVBOVertices);
	double * buff = (double*)  glMapBuffer(GL_ARRAY_BUFFER,GL_READ_ONLY);
	GLint index = (x+y*width)*3+2;
	result = (MY_DATATYPE)buff[index];
	GLboolean success = glUnmapBuffer(GL_ARRAY_BUFFER);
	glBindBuffer(GL_ARRAY_BUFFER,0);
	return result;
}

void MillingMachine3C::BindBuffer()
{
	glBindBuffer(GL_ARRAY_BUFFER,this->m_nVBOVertices);
	this->buffer = (double*) glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY_ARB);
}

void MillingMachine3C::SetValueAtWithoutFlushing(int x,int y,MY_DATATYPE value)
{
	if(value>=(MY_DATATYPE)this->initDepth)
		return;
	UINT index = (x+y*width)*3+2;
	double v = (double)value;
	buffer[index] = v;
}

void MillingMachine3C::UnbindBuffer()
{
	GLboolean success = glUnmapBuffer(GL_ARRAY_BUFFER);
	glBindBuffer(GL_ARRAY_BUFFER,0);
	this->buffer = NULL;
}

void MillingMachine3C::Draw()
{
	/*glColor3f(1.0,0.0,0.0);
	glColor3f(0.0,0.0,1.0);
	GLfloat light_ambient[] = {1.0,0.0,0.0,1.0};
	GLfloat light_diffuse[] = {1.0,0.0,0.0,1.0};
	GLfloat light_specular[] = {1.0,1.0,1.0,1.0};
	GLfloat light_position[] = {75.0,75.0,300.0,0.0};

	glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
	glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
	glLightfv(GL_LIGHT0,GL_POSITION,light_position);
	glEnable(GL_LIGHT0);*/

	glBindBuffer(GL_ARRAY_BUFFER,this->m_nVBOVertices);
	glBindBuffer(GL_COLOR_ARRAY,this->m_nColoursBufferID);
	glBindBuffer(GL_NORMAL_ARRAY,this->m_nNormalBurrerID);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,this->m_nIndicesBufferID);

	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);

	glColor3f(1, 0, 0);

	glVertexPointer(3,GL_DOUBLE,0,0);
	glColorPointer(3,GL_UNSIGNED_BYTE,0,0);
	glNormalPointer(3,GL_DOUBLE,this->normals);

	glDrawElements(GL_LINE_LOOP,this->indicesNo,GL_UNSIGNED_INT, NULL);

	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);

	glBindBuffer(GL_COLOR_ARRAY,0);
	glBindBuffer(GL_ARRAY_BUFFER,0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	//glBindBuffer(GL_NORMAL_ARRAY,0);
}
